import { changeMapFeaturesStyle, type MapFeatures } from '@/map'
import type { AuxGraphic, AuxGraphicGeometry, AuxGraphicItem } from './base'
import { auxGraphicLayer, setupAuxGraphicLayer, updateAuxGraphicLayer } from './layer'
import { AuxGraphicIndex } from './geo-index'
import { registerMapItem } from '@/map-item'
import { highlightStyle } from '@/map/highlights'
import type { Component } from 'vue'

export {
  type AuxGraphic,
  type AuxGraphicGeometry,
  auxGraphicStyle,
  auxGraphicProperties
} from './base'

export interface AuxGraphicType<AG extends AuxGraphic> {
  type: string
  typeName: string
  geometry(ag: AG): AuxGraphicGeometry[]
  feature(ag: AG, g: AuxGraphicGeometry[]): MapFeatures
  component?: Component
}

const auxGraphicTypes: Record<string, AuxGraphicType<AuxGraphic>> = {}

export function registerAuxGraphicType<AG extends AuxGraphic>(opts: AuxGraphicType<AG>) {
  auxGraphicTypes[opts.type] = opts
}

const geoIndex = new AuxGraphicIndex()
const items: Record<string, AuxGraphicItem> = {}

let idCnt = 0
function nextId(): string {
  while (true) {
    const id = `ag#${idCnt}`
    idCnt++
    if (id in items) {
      continue
    }
    return id
  }
}

export function getAuxGraphicGeometries<G extends AuxGraphicGeometry>(
  id: string
): AuxGraphicGeometry[] | null {
  const item = items[id]
  if (!item) {
    return null
  }
  return item.geometry
}

export function getAuxGraphicGeometry<G extends AuxGraphicGeometry>(id: string): G | null {
  const gs = getAuxGraphicGeometries(id)
  if (!gs) {
    return null
  }
  return (gs[0] || null) as G | null
}

export function addAuxGraphics(lst: Omit<AuxGraphic, 'id'>[]) {
  if (lst.length === 0) {
    return
  }
  for (const g of lst) {
    const aType = auxGraphicTypes[g.type]!!
    const graphic = Object.assign({ id: nextId() }, g)
    const geometry = aType.geometry(graphic)
    const feature = aType.feature(graphic, geometry)
    const item: AuxGraphicItem = { graphic, geometry, feature }
    items[item.graphic.id] = item
    geoIndex.add(graphic, geometry)
  }
  updateAuxGraphicLayer(Object.values(items))
}

export function addAuxGraphic<AG extends AuxGraphic>(g: Omit<AG, 'id'>): AG {
  const aType = auxGraphicTypes[g.type]!!
  const graphic = Object.assign({ id: nextId() }, g) as AG
  const geometry = aType.geometry(graphic)
  const feature = aType.feature(graphic, geometry)
  const item: AuxGraphicItem = { graphic, geometry, feature }
  items[item.graphic.id] = item
  geoIndex.add(graphic, geometry)
  updateAuxGraphicLayer(Object.values(items))
  return graphic
}

export function removeAuxGraphic(id: string) {
  const item = items[id]
  if (!item) {
    return
  }
  delete items[id]
  geoIndex.removeById(id)
  updateAuxGraphicLayer(Object.values(items))
}

export function updateAuxGraphic<AG extends AuxGraphic>(id: string, g: Omit<AG, 'id'>) {
  if (!(id in items)) {
    return
  }
  delete items[id]
  geoIndex.removeById(id)
  const aType = auxGraphicTypes[g.type]!!
  const graphic = Object.assign({ id }, g) as AG
  const geometry = aType.geometry(graphic)
  const feature = aType.feature(graphic, geometry)
  const item: AuxGraphicItem = { graphic, geometry, feature }
  items[item.graphic.id] = item
  geoIndex.add(graphic, geometry)
  updateAuxGraphicLayer(Object.values(items))
}

export function highlight(ag: AuxGraphic): MapFeatures | null {
  const item = items[ag.id]
  if (!item) {
    return null
  }
  const ret = changeMapFeaturesStyle(item.feature, highlightStyle.primary)
  // console.log('highlight aux-graphic', ret)
  return ret
}

export function setupAuxGraphicCore() {
  setupAuxGraphicLayer()
  registerMapItem<AuxGraphic>({
    type: 'AuxGraphic',
    typeName: '辅助图形',
    getName: (a: AuxGraphic) => a.name,
    index: {
      enable: () => auxGraphicLayer.visible,
      search(lng, lat, radius) {
        const t = geoIndex.searchOne(lng, lat, radius)
        if (!t) {
          return null
        }
        return [t]
      }
    },
    infoView(t: AuxGraphic) {
      return auxGraphicTypes[t.type]?.component || null
    },
    highlight
  })
}
